#pragma once

#include <algorithm>
#include <any>
#include <array>
#include <atomic>
#include <bit>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <charconv>
#include <chrono>
#include <climits>
#include <cmath>
#include <codecvt>
#include <compare>
#include <complex>
#include <concepts>
#include <condition_variable>
#include <csignal>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwctype>
#include <coroutine>
#include <filesystem>
#include <forward_list>
#include <fstream>
#include <functional>
#include <future>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include <optional>
#include <queue>
#include <random>
#include <regex>
#include <set>
#include <shared_mutex>
#include <span>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>
#include <version>

#include <range/v3/all.hpp>

static_assert(_LIBCPP_VERSION >= 14000);

#if COMPILER_IS_CLANG
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wold-style-cast"
    #include <stx/ext/blockingconcurrentqueue.hpp>
    #pragma clang diagnostic pop
#else
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #include <stx/ext/blockingconcurrentqueue.hpp>
    #pragma GCC diagnostic pop
#endif

// Enum value must be within [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX].

//   [*] By default: MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128.
//   [*] MAGIC_ENUM_RANGE_MIN must be less or equals than 0 and must be greater
//       than INT16_MIN.
//   {*} MAGIC_ENUM_RANGE_MAX must be greater than 0 and must be less than
//       UINT16_MAX.
//   [*] For other enum value range, redefine the macro MAGIC_ENUM_RANGE_MIN
//       and MAGIC_ENUM_RANGE_MAX.

// For example:
//   #define MAGIC_ENUM_RANGE_MIN -1
//   #define MAGIC_ENUM_RANGE_MAX 257
//   #include "magic_enum.hpp"

// WARNING:
// The greater MAGIC_ENUM_RANGE is, the slower compile time is!

// #define MAGIC_ENUM_RANGE_MIN (INT16_MIN + 1)
// #define MAGIC_ENUM_RANGE_MAX (INT16_MAX - 1)
#define MAGIC_ENUM_RANGE_MIN (-1)
#define MAGIC_ENUM_RANGE_MAX (257)
#include <stx/ext/magic_enum.hpp>

static_assert(sizeof(std::time_t) == sizeof(std::uint64_t));
static_assert(sizeof(std::size_t) == sizeof(std::uint64_t));
static_assert(sizeof(int) >= sizeof(std::int32_t));

using namespace std::literals; // NOLINT

#include <stx/core/macro.hpp>

namespace std
{
namespace fs = filesystem;
}

inline namespace stx
{
/*
LOOP (i, 10)
{
    cout(Tco::K_ERASE_CUR_LINE, i, Tco::K_RESET);
    cout(Tco::K_ERASE_CUR_LINE, Tco::K_BOLD, i, Tco::K_RESET);
    cout(Tco::K_ERASE_CUR_LINE, Tco::K_BOLD, Tco::K_RED, i, Tco::K_RESET);
}
*/
enum class Tco // Terminal Control Option
{
    K_BOLD           = 1,
    K_ERASE_CUR_LINE = 2,
    K_GREY           = 30,
    K_RED            = 31,
    K_GREEN          = 32,
    K_YELLOW         = 33,
    K_BLUE           = 34,
    K_MAGENTA        = 35,
    K_CYAN           = 36,
    K_WHITE          = 37,
    K_ON_GREY        = 40,
    K_ON_RED         = 41,
    K_ON_GREEN       = 42,
    K_ON_YELLOW      = 43,
    K_ON_BLUE        = 44,
    K_ON_MAGENTA     = 45,
    K_ON_CYAN        = 46,
    K_ON_WHITE       = 47,
    K_RESET          = 999,
};

std::ostream& operator<<(std::ostream& os, Tco const tco);

namespace views   = ::ranges::views;
namespace actions = ::ranges::actions;

template<auto Head_, auto... Tail_>
constexpr bool AllTrue = (Head_ && ... && Tail_);

template<auto Head_, auto... Tail_>
constexpr bool AnyTrue = (Head_ || ... || Tail_);

template<auto Head_, auto... Tail_>
constexpr bool NoneTrue = !AnyTrue<Head_, Tail_...>;

template<typename T>
concept IsAnyType = true;

template<typename T>
concept IsNonVoid = !std::is_void_v<T>;

template<IsAnyType T>
using Ptr = T*;

template<IsNonVoid T>
using Ref = T&;

template<IsNonVoid T>
using CRef = T const&;

template<IsNonVoid T>
using RRef = T&&;

template<IsNonVoid T>
using CrRef = T const&&;

template<IsAnyType T>
using NoCvr = std::remove_cvref_t<T>;

template<IsAnyType T>
using NoPtr = std::remove_pointer_t<NoCvr<T>>;

template<IsAnyType T>
using Type = T;

template<IsAnyType T>
constexpr auto AlwaysFalse = std::same_as<NoCvr<T>, NoCvr<T>*>;

template<IsNonVoid Head_, IsNonVoid...>
struct Head__ final
{
    using type = Head_;
};

template<IsNonVoid... Args_>
using Head = typename Head__<Args_...>::type;

using Status = int;
using Task   = std::function<void()>;

#if defined(__x86_64__) || defined(_M_X64)
constexpr auto C_CACHE_LINE_SIZE = 64uz;
#else
static_assert(false, "x64 is supported only.");
#endif

constexpr auto C_PAGE_SIZE      = 4096uz;
constexpr auto C_STATUS_SUCCESS = Status(0);
constexpr auto C_STATUS_FAILURE = Status(-1);

namespace fs
{
using std::fs::path;
using std::fs::perms;
using std::fs::file_type;
using std::fs::file_status;
using std::fs::perm_options;
using std::fs::copy_options;
using std::fs::space_info;
using std::fs::directory_entry;
using std::fs::directory_iterator;
using std::fs::recursive_directory_iterator;
} // namespace fs

[[nodiscard]] fs::path operator""_fp(char const* const sz,
                                     std::size_t const size);

struct Error final
{
    Status      code = C_STATUS_SUCCESS;
    std::string desc;

    void clear() noexcept
    {
        this->code = C_STATUS_SUCCESS;
        this->desc.clear();
    }

    [[nodiscard]] constexpr bool isGood() const noexcept
    {
        return C_STATUS_SUCCESS == this->code;
    }

    [[nodiscard]] constexpr bool isFailed() const noexcept
    {
        return C_STATUS_SUCCESS != this->code;
    }

    void set(Status const new_code) noexcept
    {
        this->code = new_code;
        this->desc.clear();
    }

    void set(std::string s_desc) noexcept
    {
        this->code = C_STATUS_FAILURE;
        this->desc = std::move(s_desc);
    }

    void set(Status const new_code, std::string new_desc) noexcept
    {
        this->code = new_code;
        this->desc = std::move(new_desc);
    }
};

TLS_VAR(gLastError, Error{});
CONSTINIT_GLOBAL_VAR(gLoggerIsEnabled__, std::atomic_bool(true));

#ifdef DEBUG
CONSTINIT_GLOBAL_VAR(gLogLevel, std::atomic_int(0));
CONSTINIT_GLOBAL_VAR(gCopyLogsToTerminal, std::atomic_bool(true));
#else
CONSTINIT_GLOBAL_VAR(gLogLevel, std::atomic_int(1));
CONSTINIT_GLOBAL_VAR(gCopyLogsToTerminal, std::atomic_bool(false));
#endif

/*
Benchmark:
----------
5986 ms: std::string::resize
2113 ms: std::string::resize_and_overwrite
3131 ms: std::vector<char>::resize
1016 ms: Buffer::resize
*/
inline void resizeDefaultInit(auto& mm, std::size_t const new_size)
{
    if constexpr (requires {
                      mm.resize_and_overwrite(
                          new_size, [&](auto, auto) { return mm.size(); });
                  })
    {
        mm.resize_and_overwrite(new_size, [&](auto, auto) { return new_size; });
    }
    else if constexpr (requires { mm.resize(new_size); })
    {
        mm.resize(new_size);
    }
    else
    {
        static_assert(AlwaysFalse<decltype(mm)>);
    }
}

inline void unused(auto const&) noexcept
{}

// initPosixEnv() must be called in main thread!
void initPosixEnv();
void randomize(void* const obuf, std::size_t const obuf_size);
void daemonize();

void                           setOsThreadTag(char const* const sz_tag);
[[nodiscard]] std::string_view getOsThreadTag();

[[nodiscard]] std::size_t getOsPid() noexcept;
[[nodiscard]] std::size_t getOsTid() noexcept;
[[nodiscard]] std::size_t getPageSize() noexcept;
[[nodiscard]] std::string getExePathByPid(std::string_view const sv_pid);
[[nodiscard]] std::string getExePathByPid(std::size_t const pid);
[[nodiscard]] std::size_t getTotalNumberOfCores() noexcept;
[[nodiscard]] std::string getCompileDateTime__(std::string_view const sv_date,
                                               std::string_view const sv_time);

struct InvalidType final
{};

class ScopeExitExecutor__ final
{
    Task fn_task_{};

public:
    explicit ScopeExitExecutor__(Task fn_task);
    ~ScopeExitExecutor__();

    NON_COPYABLE(ScopeExitExecutor__);
    NON_MOVABLE(ScopeExitExecutor__);
};

// auto q = NonblockingLockFreeQueue<int>{};
// q.enqueue(25);
// auto item = 0;
// auto found = q.try_dequeue(item);
// assert(found && 25 == item);
template<IsNonVoid T>
using NonblockingLockFreeQueue = moodycamel::ConcurrentQueue<T>;

// BlockingLockFreeQueue is just a wrapper of NonblockingLockFreeQueue.

// auto q = BlockingLockFreeQueue<int>{};
// auto item = 123;
// q.enqueue(item);
// q.wait_dequeue(item);
// assert(123 == item);
// item = 456;
// q.enqueue(item);
// assert(q.wait_dequeue_timed(item, 1s));
// assert(456 == item);
// q.enqueue(25);
// auto item = 0;
// auto found = q.try_dequeue(item);
// assert(found && 25 == item);
template<IsNonVoid T>
using BlockingLockFreeQueue = moodycamel::BlockingConcurrentQueue<T>;

GLOBAL_VAR(gLogs, BlockingLockFreeQueue<std::string>{});

[[nodiscard]] constexpr std::int8_t
operator""_i8(unsigned long long const n) noexcept
{
    return static_cast<std::int8_t>(n);
}

[[nodiscard]] constexpr std::int16_t
operator""_i16(unsigned long long const n) noexcept
{
    return static_cast<std::int16_t>(n);
}

[[nodiscard]] constexpr std::int32_t
operator""_i32(unsigned long long const n) noexcept
{
    return static_cast<std::int32_t>(n);
}

[[nodiscard]] constexpr std::int64_t
operator""_i64(unsigned long long const n) noexcept
{
    return static_cast<std::int64_t>(n);
}

[[nodiscard]] constexpr std::uint8_t
operator""_u8(unsigned long long const n) noexcept
{
    return static_cast<std::uint8_t>(n);
}

[[nodiscard]] constexpr std::uint16_t
operator""_u16(unsigned long long const n) noexcept
{
    return static_cast<std::uint16_t>(n);
}

[[nodiscard]] constexpr std::uint32_t
operator""_u32(unsigned long long const n) noexcept
{
    return static_cast<std::uint32_t>(n);
}

[[nodiscard]] constexpr std::uint64_t
operator""_u64(unsigned long long const n) noexcept
{
    return static_cast<std::uint64_t>(n);
}

[[nodiscard]] constexpr std::uint64_t
operator""_Ki(unsigned long long const n) noexcept
{
    return 1024 * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Mi(unsigned long long const n) noexcept
{
    return 1024_Ki * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Gi(unsigned long long const n) noexcept
{
    return 1024_Mi * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Ti(unsigned long long const n) noexcept
{
    return 1024_Gi * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Pi(unsigned long long const n) noexcept
{
    return 1024_Ti * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Ei(unsigned long long const n) noexcept
{
    return 1024_Pi * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Zi(unsigned long long const n) noexcept
{
    return 1024_Ei * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_K(unsigned long long const n) noexcept
{
    return 1000 * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_M(unsigned long long const n) noexcept
{
    return 1000_K * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_G(unsigned long long const n) noexcept
{
    return 1000_M * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_T(unsigned long long const n) noexcept
{
    return 1000_G * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_P(unsigned long long const n) noexcept
{
    return 1000_T * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_E(unsigned long long const n) noexcept
{
    return 1000_P * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Z(unsigned long long const n) noexcept
{
    return 1000_E * n;
}

[[nodiscard]] constexpr std::uint64_t
operator""_Y(unsigned long long const n) noexcept
{
    return 1000_Z * n;
}
} // namespace stx